<template>
  <div class="container">
    <div class="drag-tree-table" ref="table" v-bind:class="{border:border !== undefined}">
      <div class="drag-tree-table-header">
        <column
            v-for="(item, index) in data.columns"
            :width="item.width"
            :flex="item.flex"
            :border="border === undefined ? resize : border"
            v-bind:class="['align-' + item.titleAlign, 'colIndex' + index]"
            :key="index">
          <input
              v-if="item.type == 'checkbox'"
              class="checkbox"
              type="checkbox"
              @click="onCheckAll($event, item.onChange)">
          <span v-else v-html="item.title">
            </span>
          <div class="resize-line" @mousedown="mousedown(index, $event)" v-show="resize!== undefined">

          </div>
        </column>
      </div>
      <div class="drag-tree-table-body"
           v-bind:style="bodyStyle"
           @dragover="draging"
           @dragend="drop"
           :class="isDraing ? 'is-draging' : '' ">
        <row depth="0" :columns="data.columns" :isdraggable="isdraggable"
             :links="links"
             :model="item"
             v-for="(item, index) in data.lists"
             :custom_field="custom_field"
             :onCheck="onSingleCheckChange"
             :border="border === undefined ? resize : border"
             :isContainChildren="isContainChildren"
             :key="index"
             :ref="'rowItem'+ index">
        </row>
      </div>
      <div class="drag-line">
      </div>
    </div>
  </div>
</template>

<script>
/* eslint-disable */
import {getNavigationLink} from '@/api/menu-navigation';
import row from './row.vue';
import column from './column.vue';
import space from './space.vue';
import func from './func';

document.body.ondrop = function (event) {
  event.preventDefault();
  event.stopPropagation();
};
export default {
  name: 'dragTreeTable',
  components: {
    row,
    column,
    space
  },
  computed: {
    bodyStyle () {
      return {
        overflow: (this.fixed !== undefined && this.fixed !== false) ? 'auto' : 'hidden',
        height: (this.fixed !== undefined && this.fixed !== false) ? (this.height) + 'px' : 'auto'
      };
    },
    siteId () {
      return this.$store.state.user.siteId;
    }
  },
  props: {
    isdraggable: {
      type: Boolean,
      default: true
    },
    data: Object,
    onDrag: Function,
    fixed: String | Boolean,
    height: String | Number,
    border: String,
    onlySameLevelCanDrag: String,
    hightRowChange: String,
    resize: String,
    beforeDragOver: Function
  },
  data () {
    return {
      links: [],
      dragX: 0,
      dragY: 0,
      dragId: '',
      targetId: '',
      whereInsert: '',
      isDraing: false,
      custom_field: {
        id: 'id',
        parent_id: 'parent_id',
        order: 'order',
        lists: 'lists',
        open: 'open',
        checked: 'checked',
        highlight: 'highlight'
      },
      onCheckChange: null,
      isContainChildren: false,
      mouse: {
        status: 0,
        startX: 0,
        curColWidth: 0,
        curIndex: 0
      }
    };
  },
  methods: {
    draging (e) {
      this.isDraing = true;
      if (e.pageX == this.dragX && e.pageY == this.dragY) {
        return;
      }
      this.dragX = e.pageX;
      this.dragY = e.clientY;
      this.filter(e.pageX, e.clientY);
      if (e.clientY < 100) {
        window.scrollTo(0, scrollY - 6);
      } else if (e.clientY > (document.body.clientHeight - 160)) {
        window.scrollTo(0, scrollY + 6);
      }
    },
    submit () {
      for (let i=0; i<this.data.lists.length;i++) {
        this.$refs['rowItem'+i][0].submit();
      }
    },
    drop (event) {
      func.clearHoverStatus();
      this.resetTreeData();
      this.isDraing = false;
      if (this.targetId !== undefined) {
        if (this.hightRowChange !== undefined) {
          this.$nextTick(() => {
            var rowEle = document.querySelector('[tree-id=\'' + window.dragId + '\']');
            rowEle.style.backgroundColor = 'rgba(64,158,255,0.5)';
            setTimeout(() => {
              rowEle.style.backgroundColor = 'rgba(64,158,255,0)';
            }, 2000);
          });
        }
      }
    },
    // 查找匹配的行，处理拖拽样式
    filter (x, y) {
      var rows = document.querySelectorAll('.tree-row');
      this.targetId = undefined;
      const dragRect = window.dragParentNode.getBoundingClientRect();
      const dragW = dragRect.left + window.dragParentNode.clientWidth;
      const dragH = dragRect.top + window.dragParentNode.clientHeight;
      if (x >= dragRect.left && x <= dragW && y >= dragRect.top && y <= dragH) {
        // 当前正在拖拽原始块不允许插入
        return;
      }
      let hoverBlock;
      let targetId;
      let whereInsert = '';

      for (let i = 0; i < rows.length; i++) {
        const row = rows[i];
        const rect = row.getBoundingClientRect();
        const rx = rect.left;
        const ry = rect.top;
        const rw = row.clientWidth;
        const rh = row.clientHeight;
        if (x > rx && x < (rx + rw) && y > ry && y < (ry + rh)) {
          const diffY = y - ry;
          const pId = row.getAttribute('tree-p-id');
          // 不允许改变层级结构，只能改变上下顺序逻辑
          if (this.onlySameLevelCanDrag !== undefined && pId !== window.dragPId) {
            return;
          }
          targetId = row.getAttribute('tree-id');
          hoverBlock = row.children[row.children.length - 1];
          var rowHeight = row.offsetHeight;
          if (diffY / rowHeight > 3 / 4) {
            whereInsert = 'bottom';
          } else if (diffY / rowHeight > 1 / 4) {
            if (this.onlySameLevelCanDrag !== undefined) {
              // 不允许改变层级结构，只能改变上下顺序逻辑
              return;
            }
            whereInsert = 'center';
          } else {
            whereInsert = 'top';
          }
          break;
        }
      }
      if (targetId === undefined) {
        // 匹配不到清空上一个状态
        func.clearHoverStatus();
        let whereInsert = '';
        return;
      }

      let canDrag = true;
      if (this.beforeDragOver) {
        const curRow = this.getItemById(this.data.lists, window.dragId);
        const targetRow = this.getItemById(this.data.lists, targetId);
        canDrag = this.beforeDragOver(curRow, targetRow, whereInsert);
      }
      if (canDrag == false) {
        return;
      }
      hoverBlock.style.display = 'block';
      var rowHeight = row.offsetHeight;
      if (whereInsert == 'bottom') {
        if (hoverBlock.children[2].style.opacity !== '0.5') {
          func.clearHoverStatus();
          hoverBlock.children[2].style.opacity = 0.5;
        }
      } else if (whereInsert == 'center') {
        if (hoverBlock.children[1].style.opacity !== '0.5') {
          func.clearHoverStatus();
          hoverBlock.children[1].style.opacity = 0.5;
        }
      } else {
        if (hoverBlock.children[0].style.opacity !== '0.5') {
          func.clearHoverStatus();
          hoverBlock.children[0].style.opacity = 0.5;
        }
      }

      this.targetId = targetId;
      this.whereInsert = whereInsert;
    },
    resetTreeData () {
      if (this.targetId === undefined) {
        return;
      }
      const listKey = this.custom_field.lists;
      const parentIdKey = this.custom_field.parent_id;
      const idKey = this.custom_field.id;
      const newList = [];
      const curList = this.data.lists;
      const _this = this;
      let curDragItem = null;
      let taggetItem = null;

      function pushData (curList, needPushList) {
        for (let i = 0; i < curList.length; i++) {
          const item = curList[i];
          var obj = func.deepClone(item);
          obj[listKey] = [];
          if (_this.targetId == item[idKey]) {
            curDragItem = _this.getItemById(_this.data.lists, window.dragId);
            taggetItem = _this.getItemById(_this.data.lists, _this.targetId);
            if (_this.whereInsert === 'top') {
              curDragItem[parentIdKey] = item[parentIdKey];
              needPushList.push(curDragItem);
              needPushList.push(obj);
            } else if (_this.whereInsert === 'center') {
              curDragItem[parentIdKey] = item[idKey];
              obj.open = true;
              obj[listKey].push(curDragItem);
              needPushList.push(obj);
            } else {
              curDragItem[parentIdKey] = item[parentIdKey];
              needPushList.push(obj);
              needPushList.push(curDragItem);
            }
          } else {
            if (window.dragId != item[idKey]) {
              needPushList.push(obj);
            }
          }
          if (item[listKey] && item[listKey].length) {
            pushData(item[listKey], obj[listKey]);
          }
        }
      }

      pushData(curList, newList);
      this.resetOrder(newList);
      this.onDrag(newList, curDragItem, taggetItem, _this.whereInsert);
    },
    // 重置所有数据的顺序order
    resetOrder (list) {
      const listKey = this.custom_field.lists;
      for (var i = 0; i < list.length; i++) {
        list[i][this.custom_field.order] = i;
        if (list[i][listKey] && list[i][listKey].length) {
          this.resetOrder(list[i][listKey]);
        }
      }
    },
    // 根据id获取当前行
    getItemById (lists, id) {
      var curItem = null;
      const listKey = this.custom_field.lists;
      const idKey = this.custom_field.id;

      function getchild (curList) {
        for (let i = 0; i < curList.length; i++) {
          var item = curList[i];
          if (item[idKey] == id) {
            curItem = JSON.parse(JSON.stringify(item));
            break;
          } else if (item[listKey] && item[listKey].length) {
            getchild(item[listKey]);
          }
        }
      }

      getchild(lists);
      return curItem;
    },
    // 对外暴漏
    DelById (id) {
      const listKey = this.custom_field.lists;
      const orderKey = this.custom_field.order;
      const idKey = this.custom_field.id;
      const newList = [];
      const curList = this.data.lists;

      function pushData (curList, needPushList) {
        let order = 0;
        for (let i = 0; i < curList.length; i++) {
          const item = curList[i];
          if (item[idKey] != id) {
            var obj = func.deepClone(item);
            obj[orderKey] = order;
            obj[listKey] = [];
            needPushList.push(obj);
            order++;
            if (item[listKey] && item[listKey].length) {
              pushData(item[listKey], obj[listKey]);
            }
          }
        }
      }

      pushData(curList, newList);
      return newList;
    },
    // 递归设置属性,只允许设置组件内置属性
    deepSetAttr (key, val, list, ids) {
      const listKey = this.custom_field.lists;
      for (var i = 0; i < list.length; i++) {
        if (ids !== undefined) {
          if (ids.includes(list[i][this.custom_field['id']])) {
            list[i][this.custom_field[key]] = val;
          }
        } else {
          list[i][this.custom_field[key]] = val;
        }
        if (list[i][listKey] && list[i][listKey].length) {
          this.deepSetAttr(key, val, list[i][listKey], ids);
        }
      }
    },
    ZipAll (id, deep = true) {
      let list = func.deepClone(this.data.lists);
      this.deepSetAttr('open', false, list);
      this.data.lists = list;
    },
    OpenAll (id, deep = true) {
      let list = func.deepClone(this.data.lists);
      this.deepSetAttr('open', true, list);
      this.data.lists = list;
    },
    GetLevelById (id) {
      var row = this.$refs.table.querySelector('[tree-id="' + id + '"]');
      var level = row.getAttribute('data-level') * 1;
      return level;
    },
    HighlightRow (id, isHighlight = true, deep = false) {
      let list = func.deepClone(this.data.lists);
      let ids = [id];
      if (deep == true) {
        ids = ids.concat(this.GetChildIds(id, true));
      }
      this.deepSetAttr('highlight', isHighlight, list, ids);
      this.data.lists = list;
    },
    AddRow (pId, data) {
      const deepList = func.deepClone(this.data.lists);
      var _this = this;

      function deep (list) {
        const listKey = _this.custom_field.lists;
        for (var i = 0; i < list.length; i++) {
          if (list[i][_this.custom_field['id']] == pId) {
            list[i][_this.custom_field['open']] = true;
            var newRow = Object.assign({}, data);
            newRow[_this.custom_field['parent_id']] = pId;
            if (list[i][listKey]) {
              newRow[_this.custom_field['order']] = list[i][listKey].length;
              list[i][listKey].push(newRow);
            } else {
              list[i][listKey] = [];
              newRow[_this.custom_field['order']] = 0;
              list[i][listKey].push(newRow);
            }
          }
          if (list[i][listKey] && list[i][listKey].length) {
            deep(list[i][listKey]);
          }
        }
      }

      deep(deepList);
      this.data.lists = deepList;
    },
    EditRow (id, data) {
      const deepList = func.deepClone(this.data.lists);
      var _this = this;

      function deep (list) {
        const listKey = _this.custom_field.lists;
        for (var i = 0; i < list.length; i++) {
          if (list[i][_this.custom_field['id']] == id) {
            var newRow = Object.assign({}, list[i], data);
            list[i] = newRow;
          }
          if (list[i][listKey] && list[i][listKey].length) {
            deep(list[i][listKey]);
          }
        }
      }

      deep(deepList);
      this.data.lists = deepList;
    },
    GetChildIds (id, deep = true) {
      let ids = [];
      const _this = this;

      function getChilds (list, id) {
        const listKey = _this.custom_field.lists;
        for (var i = 0; i < list.length; i++) {
          let currentPid = '';
          let pid = list[i][_this.custom_field['parent_id']];
          if (id == pid) {
            currentPid = list[i][_this.custom_field['id']];
            ids.push(currentPid);
          } else {
            currentPid = id;
          }
          if (deep == true || id == currentPid) {
            if (list[i][listKey] && list[i][listKey].length) {
              getChilds(list[i][listKey], currentPid);
            }
          }
        }
      }

      getChilds(this.data.lists, id);
      return ids;
    },
    // 全选按钮事件
    onCheckAll (evt, func) {
      this.setAllCheckData(this.data.lists, !!evt.target.checked);
      const checkedList = this.getCheckedList(this.data.lists);
      func && func(checkedList);
    },
    // 单个CheckBox勾选触发
    onSingleCheckChange () {
      const checkedList = this.getCheckedList(this.data.lists);
      this.onCheckChange && this.onCheckChange(checkedList);
    },
    // 根据flag批量处理数据
    setAllCheckData (curList, flag) {
      const listKey = this.custom_field.lists;
      for (let i = 0; i < curList.length; i++) {
        var item = curList[i];
        this.$set(item, 'checked', flag);
        if (item[listKey] && item[listKey].length) {
          this.setAllCheckData(item[listKey], flag);
        }
      }
    },
    // 获取所有选中的行
    getCheckedList (lists) {
      const listKey = this.custom_field.lists;
      var checkedList = [];
      const deepList = func.deepClone(lists);

      function getchild (curList) {
        for (let i = 0; i < curList.length; i++) {
          var item = curList[i];
          if (item.checked && item.isShowCheckbox != false) {
            checkedList.push(item);
          }
          if (item[listKey] && item[listKey].length) {
            getchild(item[listKey]);
          }
        }
      }

      getchild(deepList);
      return checkedList;
    },
    mousedown (curIndex, e) {
      const startX = e.target.getBoundingClientRect().x;
      const curColWidth = e.target.parentElement.offsetWidth;
      this.mouse = {
        status: 1,
        startX,
        curIndex,
        curColWidth
      };
    },
    initSelectData () {
      getNavigationLink(this.siteId).then(res => {
        console.log(res.data);
        this.links = res.data;
        // this.links = res.data.filter(res => res.code !== 'theme_auto');
      });
    },
  },
  created () {
    this.initSelectData();
  },
  mounted () {
    if (this.data.custom_field) {
      this.custom_field = Object.assign({}, this.custom_field, this.data.custom_field);
    }
    setTimeout(() => {
      this.data.columns.map((item) => {
        if (item.type == 'checkbox') {
          this.onCheckChange = item.onChange;
          this.isContainChildren = item.isContainChildren;
        }
      });
    }, 100);
    window.addEventListener('mouseup', e => {
      if (this.mouse.status) {
        const curX = e.clientX;
        var line = document.querySelector('.drag-line');
        line.style.left = '-10000px';
        this.mouse.status = 0;
        const curWidth = this.mouse.curColWidth;
        const subWidth = curX - this.mouse.startX;
        const lastWidth = curWidth + subWidth;
        const cols = document.querySelectorAll('.colIndex' + this.mouse.curIndex);
        for (let index = 0; index < cols.length; index++) {
          const element = cols[index];
          element.style.width = lastWidth + 'px';
        }
        // 更新数据源
        this.data.columns[this.mouse.curIndex].width = lastWidth;
      }
    });
    window.addEventListener('mousemove', e => {
      if (this.mouse.status) {
        const endX = e.clientX;
        const tableLeft = document.querySelector('.drag-tree-table').getBoundingClientRect().left;
        var line = document.querySelector('.drag-line');
        line.style.left = endX - tableLeft + 'px';
      }
    });
  }
};
</script>

<style lang="scss">
.container {
  width: 1200px;
  overflow-x: scroll;
  .drag-tree-table {
    width: 1200px;
    position: relative;
    margin: 20px 0;
    color: #606266;
    font-size: 12px;

    &.border {
      border: 1px solid #eee;
      border-right: none;
    }
  }
}

.drag-line {
  position: absolute;
  top: 0;
  left: -1000px;
  height: 100%;
  width: 1px;
  background: #ccc;
}

.drag-tree-table-header {
  display: flex;
  // padding: 15px 10px;
  background: #f5f7fa;
  height: 66px;
  line-height: 36px;
  box-sizing: border-box;
  font-weight: 600;

  .align-left {
    text-align: left;
  }

  .align-right {
    text-align: right;
  }

  .align-center {
    text-align: center;
  }

  .tree-column {
    user-select: none;
  }
}

.tree-icon-hidden {
  visibility: hidden;
}

.is-draging .tree-row:hover {
  background: transparent !important;
}

.tree-row {
  background-color: rgba(64, 158, 255, 0);
  transition: background-color 0.5s linear;
}
</style>
